package main;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;

import tool.ColorPicker;
import tool.Fill;
import tool.Pen;
import tool.Tool;

import control.Button;
import control.ControlCommand;
import control.GroupControl;
import control.HorizontalSlider;

public class DrawingCanvas extends Canvas implements Runnable {

	GroupControl toolBox;
	MainMidlet midlet;
	int pX = 0;
	int pY = 0;
	long time = 0;
	int viewportX = 0;
	int viewportY = 0;
	Image img;
	int[] backupData;
	int[] data;
	int cellW = 25;
	int nCol;
	int nRow;
	static int backgroundColor = 0xffFFffFF;
	static int gridColor = 0xffDDddDD;
	static boolean showGrid = true;
	int currentColor = 0;
	int secondaryColor = 0;
	private boolean isInterrupting;
	private boolean isPressed = false;
	HorizontalSlider zoomSlider;
	Button btnColor;
	Button btnSecondaryColor;
	Button btnPen;
	Button btnFill;
	Button btnPicker;
	Tool tool;
	private boolean viewMode = false;

	
	public DrawingCanvas getInstance() {
		return this;
	}
	public void newData(int n){
		data = new int[n];
		backupData = new int[n];
	}
	public void backUpData(){
		for (int i = 0; i < data.length; i++) {
			backupData[i] = data[i]; 
		}
	}
	public void retoreData(){
		for (int i = 0; i < data.length; i++) {
			int temp = backupData[i]; 
			backupData[i] = data[i]; 
			data[i] = temp;
		}
	}
	public void SetCellW(int cw) {
		double scale = (double) cw / cellW;
		viewportX = (int) ((viewportX - 120) * scale) + 120;
		viewportY = (int) ((viewportY - 200) * scale) + 200;
		cellW = cw;
		endTool();
	}

	public void endTool() {
		tool = null;
		btnPen.setTogled(false);
		btnFill.setTogled(false);
		btnPicker.setTogled(false);
	}

	public DrawingCanvas(MainMidlet mainMidlet, int c, int r) {
		setFullScreenMode(true);
		midlet = mainMidlet;
		nCol = c;
		nRow = r;
		//data = new int[c * r];
		newData(c*r);
		for (int i = 0; i < data.length; i++) {
			data[i] = 0;
		}

		toolBox = new GroupControl(10);
		Button btn = new Button(0, 112, 0);
		btn.setCommand(new ControlCommand() {
			public void Do() {
				int cw = cellW + 10;
				if (cw > 40) {
					cw = 40;
				}
				SetCellW(cw);
				zoomSlider.setValue(cellW);
			}
		});
		toolBox.setControl(0, btn);

		btn = new Button(1, 32, 0);
		btn.setCommand(new ControlCommand() {
			public void Do() {
				int cw = cellW - 10;
				if (cw < 3) {
					cw = 3;
				}
				SetCellW(cw);
				zoomSlider.setValue(cellW);
			}
		});
		toolBox.setControl(1, btn);

		zoomSlider = new HorizontalSlider(3, 40, 64, 3, 48, 26);
		zoomSlider.setValue(cellW);
		toolBox.setControl(2, zoomSlider);
		toolBox.get(2).setCommand(new ControlCommand() {
			public void Do() {
				SetCellW(zoomSlider.getValue());
			}
		});

		btn = new Button(2, 0, 0);
		btn.setCommand(new ControlCommand() {
			public void Do() {
				endTool();
				midlet.showMenu();
			}
		});
		toolBox.setControl(3, btn);

		btnColor = new Button(3, 204, 368);
		btnColor.setTogled(true);
		btnColor.setCommand(new ControlCommand() {
			public void Do() {
				//midlet.setColor(currentColor);
				midlet.showColorCanvas(new ColorHandler() {
					public void setColor(int newColor) {
						getInstance().setColor(newColor,true);
					}
					public int getColor() {
						return currentColor;
					}
				});
			}
		});
		toolBox.setControl(4, btnColor);
		
		btnSecondaryColor = new Button(3, 168, 368);
		btnSecondaryColor.setTogled(true);
		btnSecondaryColor.setCommand(new ControlCommand() {
			public void Do() {
				setColor(secondaryColor,true);
			}
		});
		toolBox.setControl(5, btnSecondaryColor);

		btnPen = new Button(4, 40, 368,8);
		btnPen.setCommand(new ControlCommand() {
			public void Do() {
				
				if(btnPen.isTogled()){
					endTool();
					return;
				}
				endTool();
				btnPen.setTogled(true);
				tool = new Pen(getInstance());
			}
		});
		toolBox.setControl(6, btnPen);

		btnFill = new Button(5, 80, 368,8);
		btnFill.setCommand(new ControlCommand() {
			public void Do() {
				
				if(btnFill.isTogled()){
					endTool();
					return;
				}
				endTool();
				btnFill.setTogled(true);
				tool = new Fill(getInstance());
			}
		});
		toolBox.setControl(7, btnFill);

		btnPicker = new Button(6, 120, 368,8);
		btnPicker.setCommand(new ControlCommand() {
			public void Do() {
				if(btnPicker.isTogled()){
					endTool();
					return;
				}
				endTool();
				btnPicker.setTogled(true);
				tool = new ColorPicker(getInstance());
			}
		});
		toolBox.setControl(8, btnPicker);
		
		toolBox.setControl(9, new Button(7, 0, 368));
		toolBox.get(9).setCommand(new ControlCommand() {
			public void Do() {
				retoreData();
			}
		});
		/*
		toolBox.setControl(9,  new Button(8, 208, 0));
		toolBox.get(9).setCommand(new ControlCommand() {
			public void Do() {
				//viewMode = !viewMode;
			}
		});
		*/

		setColor(0xff000000,true);
	}

	protected void pointerPressed(int x, int y) {
		isDraged = false;
		if (toolBox.pointerPressed(x, y)) {
			repaint();
			return;
		}
		if (tool != null) {
			if (tool.pointerPressed(x, y)) {
				endTool();
			}
			repaint();
			return;
		}
		pX = x;
		pY = y;
		time = System.currentTimeMillis();
		isPressed = true;

		toolBox.pointerPressed(x, y);
		repaint();
	}

	protected void pointerReleased(int x, int y) {

		if(viewMode){
			if(x>=240-img.getWidth() && y<= img.getHeight()){
				viewMode = false;
				repaint();
				return;
			}
		}else{
			if(x>=208 && y<= 32){
				viewMode = true;
				repaint();
				return;
			}
		}		
		if (toolBox.pointerReleased(x, y)) {
			repaint();
			return;
		}
		if (tool != null) {
			if (tool.pointerReleased(x, y)) {
				endTool();
			}
			repaint();
			return;
		}
		if (isPressed) {
			backUpData();
			int i = getCol(x);
			int j = getRow(y);
			setData(i, j);
			isPressed = false;
			repaint();
		}
		toolBox.pointerReleased(x, y);
	}

	boolean isDraged = false;

	protected void pointerDragged(int x, int y) {

		if (toolBox.pointerPressed(x, y)) {
			repaint();
			isPressed = false;
			return;
		}
		if (tool != null) {
			if (tool.pointerDragged(x, y)) {
				endTool();
			}
			repaint();
			return;
		}
		if ((Math.abs(x - pX) + Math.abs(y - pY) > 20 && isPressed) || isDraged) {
			viewportX += x - pX;
			viewportY += y - pY;
			pX = x;
			pY = y;
			isPressed = false;
			isDraged = true;
			repaint();

		} else {

		}
	}

	protected void paint(Graphics g) {
		// TODO Auto-generated method stub
		g.setColor(0xff888888);
		int w = getWidth();
		int h = getHeight();
		int imgW = nCol * cellW;
		int imgH = nRow * cellW;
		g.fillRect(0, 0, w, h);
		g.setColor(backgroundColor);
		g.fillRect(viewportX, viewportY, imgW, imgH);

		int sC = 0;
		if (viewportX < 0) {
			sC = -viewportX / cellW;
		}
		int sR = 0;
		if (viewportY < 0) {
			sR = -viewportY / cellW;
		}

		int eC = nCol;
		if (imgW + viewportX > w) {
			eC = (-viewportX + w) / cellW + 1;
		}
		int eR = nRow;
		if (imgH + viewportY > h) {
			eR = (-viewportY + h) / cellW + 1;
		}

		for (int i = sC; i < eC; i++) {
			for (int j = sR; j < eR; j++) {
				int color = getData(i, j);
				if (color != 0) {
					g.setColor(color);
					g.fillRect(viewportX + i * cellW, viewportY + j * cellW,
							cellW, cellW);
				}
			}
		}
		if (showGrid && cellW > 15) {
			
			g.setColor(gridColor);
			for (int i = sC + 1; i < eC; i++) {
				g.drawLine(viewportX + i * cellW, viewportY, viewportX + i
						* cellW, viewportY + imgH);
			}
			for (int j = sR + 1; j < eR; j++) {
				g.drawLine(viewportX, viewportY + j * cellW, viewportX + imgW,
						viewportY + j * cellW);
			}
		}

		if (isPressed) {
			g.setColor(0xffffff00);
			int i = (pX - viewportX) / cellW;
			int j = (pY - viewportY) / cellW;
			if (i >= 0 && i < nCol && j >= 0 && j < nRow) {
				g.drawRect(viewportX + i * cellW, viewportY + j * cellW, cellW,
						cellW);
			}
			if (i >= 0 && i < nCol && j >= 0 && j < nRow) {
				g.drawRect(viewportX + i * cellW - 1,
						viewportY + j * cellW - 1, cellW + 2, cellW + 2);
			}
		}

		g.setColor(0xffcccccc);
		//g.fillRect(0, 368, 240, 33);
		g.fillRoundRect(0, 366,240, 40, 10, 10);
		
		g.setColor(0xff444444);
		g.drawRoundRect(0, 366,240, 40, 10, 10);
		g.setColor(currentColor);
		g.fillRoundRect(155, 393,90, 8, 10, 10);
		if (img != null) {
			g.setColor(backgroundColor);
			if(viewMode){
				int iw =img.getWidth();
				int ih= img.getHeight();
				int ix = 240-iw;
				g.fillRect(ix, 0, iw, ih);
				g.setColor(currentColor);
				g.drawRect(ix-1, -1, iw+1, ih+1);
				g.drawImage(img,240-iw, 0, 0);
				int vx = -(viewportX-120)/cellW;
				int vy = -(viewportY-200)/cellW;
				if(vx<0){
					vx = 0;
				}
				if(vy>ih){
					vy = ih;
				}
				g.drawLine(ix+vx, vy-5, ix+vx, vy+5);
				g.drawLine(ix+vx-5, vy, ix+vx+5, vy);
				
			}else{
				int iw =img.getWidth();
				int ih= img.getHeight();
				if(iw<=32){
					g.fillRect(240-iw, 0, iw, ih);
					g.setColor(currentColor);
					g.drawRect(240-iw-1, -1, iw+1, ih+1);
					g.drawImage(img,240-iw, 0, 0);
				}
				else{
					g.fillRect(208, 0, 32, 32);
					g.setColor(currentColor);
					g.drawRect(207, -1, 33, 33);
					
					int vx = -(viewportX-120)/cellW-16;
					int vy = -(viewportY-200)/cellW-16;
					if(vx<0){
						vx = 0;
					}else if(vx>img.getWidth()-32){
						vx = img.getWidth()-32;
					}
					if(vy<0){
						vy = 0;
					}else if(vy>img.getHeight()-32){
						vy = img.getHeight()-32;
					}
					g.drawRegion(img, vx, vy, 32, 32, 0, 208, 0, 0);
				}
				
			}
		}
		toolBox.paint(g);

	}

	public void start() {
		Thread t = new Thread(this);
		t.start();
		isInterrupting = false;
	}

	public void stop() {
		isInterrupting = true;
	}

	public void run() {
		// Graphics g = getGraphics();
		long endTime;
		while (!isInterrupting) {

			endTime = System.currentTimeMillis();

			if (isPressed && endTime - time > 500) {
				int i = (pX - viewportX) / cellW;
				int j = (pY - viewportY) / cellW;
				setColor(getData(i, j),true);
				isPressed = false;
			}
			img = Image.createRGBImage(data, nCol, nRow, true);
			repaint();
			try {

				Thread.sleep(500);

			} catch (InterruptedException ex) {
			}
		}
	}

	public void setData(int i, int j) {
		if (i >= 0 && i < nCol && j >= 0 && j < nRow) {
			data[i + j * nCol] = currentColor;
		}
	}

	public int getData(int i, int j) {
		if (i >= 0 && i < nCol && j >= 0 && j < nRow) {
			return data[i + j * nCol];
		}
		return 0;
	}

	public int getCol(int x) {
		return (x - viewportX) / cellW;
	}

	public int getRow(int y) {
		return (y - viewportY) / cellW;
	}

	public void replaceColor(){
		backUpData();
		for (int i = 0; i < data.length; i++) {
			if(data[i] == secondaryColor){
				data[i] = currentColor;
			}
		}
	}
	public void translate(int x,int y){
		//backUpData();
		for (int i = 0; i < nCol; i++) {
			for (int j = 0; j < nCol; j++) {
				backupData[i + j * nCol] = getData(i+x, j+y);
			}
		}
		retoreData();
	}
	public void setColor(int color,boolean updateSecondaryColor) {
		
		// TODO Auto-generated method stub
		if(updateSecondaryColor && color != currentColor){
			secondaryColor = currentColor;
			btnSecondaryColor.setColor(secondaryColor);
		}
		currentColor = color;
		btnColor.setColor(color);
		
	}
	public int getColor() {
		// TODO Auto-generated method stub
		/*currentColor = color;
		btnColor.setColor(color);*/
		return currentColor;
	}

	public Image getImage() {
		return Image.createRGBImage(data, nCol, nRow, true);
	}
	public Image getOpaqueImage() {
		int[] imgData = new int[data.length];
		for (int i = 0; i < imgData.length; i++) {
			if(data[i]==0){
				imgData[i] = backgroundColor;
			}else{
				imgData[i] = data[i];
			}
		}
		return Image.createRGBImage(imgData, nCol, nRow, true);
	}
	public void floodFill(int i, int j) {
		// TODO Auto-generated method stub
		int bColor = getData(i, j);
		if(bColor != currentColor){
			floodFill(bColor, i, j);
		}

	}

	public void floodFill(int bColor, int i, int j) {
		// TODO Auto-generated method stub
		if (i >= 0 && i < nCol && j >= 0 && j < nRow) {
			int color = getData(i, j);
			if (color == bColor) {
				setData(i, j);
				floodFill(bColor, i + 1, j);
				floodFill(bColor, i - 1, j);
				floodFill(bColor, i, j + 1);
				floodFill(bColor, i, j - 1);
			}
		}
	}
	
	public void saveGame(boolean auto) {
		RecordStore rs = null;
		try {
			// RecordStore.deleteRecordStore("dbx");
			rs = RecordStore.openRecordStore("db-pixel", true);

			byte[] byteData = new byte[data.length*4+1];
			
			for (int i = 0; i < data.length; i++) {
				intToByteArray(data[i], i*4, byteData);	
			}
			byteData[byteData.length-1]=(byte)(nCol+Byte.MIN_VALUE);
			data[0] = -1;
			if (rs.getNumRecords() == 0) {
				rs.addRecord(byteData, 0, byteData.length);
			}
			if(auto){
				rs.setRecord(1, byteData, 0, byteData.length);
			}
			else{
				if(rs.getNumRecords() == 1){
					rs.addRecord(byteData, 0, byteData.length);
				}
				else{
					rs.setRecord(2, byteData, 0, byteData.length);
				}
			}
		} catch (RecordStoreFullException e) {
			e.printStackTrace();
		} catch (RecordStoreNotFoundException e) {
			e.printStackTrace();
		} catch (RecordStoreException e) {
			e.printStackTrace();
		} finally {
			try {
				rs.closeRecordStore();
			} catch (Exception e) {
			}
		}
	}

	public void intToByteArray(int value, int o, byte[] output) {
		for (int i = 0; i < 4; i++) {
			int offset = (4 - 1 - i) * 8;
			output[i + o] = (byte) ((value >>> offset) & 0xFF);
		}
	}

	private static int byteArrayToInt(byte[] bytes, int o) {
		int r = 0;
		int b = bytes[o];
		if (b < 0) {
			b += 255;
		}
		r = r | (b << 24);

		b = bytes[o + 1];
		if (b < 0) {
			b += 255;
		}
		r = r | (b << 16);

		b = bytes[o + 2];
		if (b < 0) {
			b += 255;
		}
		r = r | (b << 8);

		b = bytes[o + 3];
		if (b < 0) {
			b += 255;
		}
		r = r | (b);
		return r;
	}

	public static boolean existingSavedGame(boolean auto) {
		boolean existingOrNot = false;
		RecordStore rs = null;

		try {
			rs = RecordStore.openRecordStore("db-pixel", false);
			int n = rs.getNumRecords();
			if((n > 1) ||(n==1 && auto)){
				existingOrNot = true;
			}
		} catch (RecordStoreNotFoundException e) {
			existingOrNot = false;
		} catch (Exception e) {
		} finally {
			try {
				rs.closeRecordStore();
			} catch (Exception e) {
			}
		}
		return existingOrNot;
	}

	public void loadGame(boolean auto) {
		RecordStore rs = null;
		try {

			rs = RecordStore.openRecordStore("db-pixel", true);
			int recordId;
			if(auto){
				recordId = 1;
			}else{
				recordId = 2;
			}
			byte[] byteData = rs.getRecord(recordId);
			
			int nB = byteData.length-1;
			int n = nB/4;
			newData(n);
			//.data = new int[n];
			for (int i = 0; i < data.length; i++) {
				data[i] = byteArrayToInt(byteData, i*4);
			}
			backUpData();
			nCol = byteData[nB]-Byte.MIN_VALUE;
			nRow = n/nCol;
			
		} catch (RecordStoreFullException e) {
			e.printStackTrace();
		} catch (RecordStoreNotFoundException e) {
			e.printStackTrace();
		} catch (RecordStoreException e) {
			e.printStackTrace();
		} finally {
			try {
				rs.closeRecordStore();
			} catch (Exception e) {
			}
		}

	}

}
